﻿using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace LINQAmazonProvider
{
    public class AmazonQuery<T> : IOrderedQueryable<T>, IOrderedQueryable, IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable 
    {
        AmazonProvider _provider;
        Expression _expression;

        /// <summary>
        /// Class constructor
        /// This constructor is called by the client to create the data source.
        /// </summary>
        /// <param name="provider">Provider instance to process querys</param>
        public AmazonQuery(AmazonProvider provider) 
        {
            if (provider == null) 
            {
                throw new ArgumentNullException("Provider can't be null.");
            }

            this._provider = provider;
            this._expression = Expression.Constant(this);
        }

        /// <summary>
        /// Class constructor
        /// This constructor is called by Provider.CreateQuery().
        /// </summary>
        /// <param name="provider">Provider instance to process the query</param>
        /// <param name="expression">Expression that represents the query as a tree of LINQ query operators/method calls</param>
        public AmazonQuery(AmazonProvider provider, Expression expression)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("Provider can't be null.");
            }

            if (expression == null) 
            {
                throw new ArgumentNullException("Expression can't be null.");
            }

            if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type)) 
            {
                throw new ArgumentOutOfRangeException("Expression can't be null.");
            }

            this._provider = provider; 
            this._expression = expression;
        }

               
        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            IEnumerable<T> queryResult;
            queryResult = Provider.Execute<IEnumerable<T>>(Expression);
            //queryResult = (IEnumerable<T>)Provider.Execute(Expression);
            return queryResult.GetEnumerator();
            //return (Provider.Execute<IEnumerable<T>>(Expression)).GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            IEnumerable queryResult;
            queryResult = Provider.Execute<System.Collections.IEnumerable>(Expression);

            return queryResult.GetEnumerator();
            //return (Provider.Execute<System.Collections.IEnumerable>(Expression)).GetEnumerator();
        }

        #endregion

        #region IQueryable Members
        
        /// <summary>
        ///Gets the type of the element(s) that are returned when the expression tree
        ///associated with this instance of System.Linq.IQueryable is executed.
        ///
        /// Returns:
        ///     A System.Type that represents the type of the element(s) that are returned
        ///     when the expression tree associated with this object is executed.
        /// </summary>
        public Type ElementType
        {
            get 
            { 
                return typeof(T); 
            }
        }

        /// <summary>
        ///Gets the expression tree that is associated with the instance of System.Linq.IQueryable.
        ///
        /// Returns:
        ///     The System.Linq.Expressions.Expression that is associated with this instance
        ///     of System.Linq.IQueryable.
        /// </summary>
        public System.Linq.Expressions.Expression Expression
        {
            get 
            { 
                return _expression; 
            }
        }

        /// <summary>
        ///Gets the query provider that is associated with this data source.
        ///
        ///Returns:
        ///    The System.Linq.IQueryProvider that is associated with this data source.
        /// </summary>
        public IQueryProvider Provider
        {
            get 
            { 
                return _provider; 
            }
        }

        #endregion
          
    }
}
